﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows.Input;
using Fantacid303.ProcessAffinityControl.Manager.Extensions;
using Fantacid303.ProcessAffinityControl.Config;
using System.Windows;
using Fantacid303.ProcessAffinityControl.Manager.Properties;

namespace Fantacid303.ProcessAffinityControl.Manager.MainWindowViewModels
{

	partial class ConfigurationViewModel
	{
		#region command classes
		private class MyAddNewEntryCommand : MyCommandBase<ConfigurationViewModel>
		{
			public MyAddNewEntryCommand(ConfigurationViewModel model) : base(model)
			{
			}

			public override bool CanExecute(object parameter)
			{
				return true;
			}
			public override void Execute(object parameter)
			{
				var newEntry = new ConfigurationEntryViewModel(this.Model) { Name = "<< Name >>" };
				newEntry.AddRuleCommand.Execute(null); // add a default rule
				this.Model.ConfigurationEntries.Add(newEntry);

				this.Model.StatusMessage = Messages.ConfigNotSaved;
			}
		}

		private class MyRefreshProcessListCommand : MyCommandBase<ConfigurationViewModel>
		{
			public MyRefreshProcessListCommand(ConfigurationViewModel model) : base(model)
			{
			}

			public override bool CanExecute(object parameter)
			{
				return true;
			}
			public override void Execute(object parameter)
			{
				var processes = System.Diagnostics.Process.GetProcesses().ToList();
				foreach (var process in processes)
				{
					var item = this.Model.RunningProcesses.FirstOrDefault(a => a.PID == process.Id);
					if (item == null)
					{
						item = new ProcessViewModel((uint)process.Id, this.Model) { Name = process.ProcessName };
						this.Model.RunningProcesses.Add(item);
					}

					item.Name = process.ProcessName;

					try
					{
						item.AffinityMask = (uint)process.ProcessorAffinity;
					}
					catch (Exception ex)
					{
						ex.ToString(); // prevent warning
					}
				}

				// remove processes from the list which do no longer exist
				for (var i = this.Model.RunningProcesses.Count - 1; i >= 0; i--)
				{
					var existingProcess = this.Model.RunningProcesses[i];
					if (processes.FirstOrDefault(a => a.Id == (uint)existingProcess.PID) == null)
						this.Model.RunningProcesses.RemoveAll(a => a.PID == existingProcess.PID);
				}
			}
		}

		private class MySaveCommand : MyCommandBase<ConfigurationViewModel>
		{
			public MySaveCommand(ConfigurationViewModel model) : base(model)
			{}

			public override bool CanExecute(object parameter)
			{
				return true;
			}

			private void ValidateConfiguration()
			{
				// ensure that every entry has a name
				if (this.Model.ConfigurationEntries.Where(a => a.Name.Trim() == string.Empty).Count() > 0)
					throw new InvalidConfigurationException("Every entry scenario must have a name!");

				// make sure, that every configuration entry has a unique name
				var entries = this.Model.ConfigurationEntries.GroupBy(a => a.Name.ToLower().Trim()).Where(b => b.Count() > 1).ToList();
				if (entries.Count > 0) throw new InvalidConfigurationException("Every entry name must be unique!");

				// make sure, that there is at least one active configuration
				var activeEntries = this.Model.ConfigurationEntries.Where(a => a.IsActive).ToList();
				if (activeEntries.Count==0) throw new InvalidConfigurationException("At least one entry must be active!");
				if (activeEntries.Count>1) throw new InvalidConfigurationException("Onle one active entry is allowed!");

				foreach (var entry in this.Model.ConfigurationEntries)
				{
					// enusre, that every rule has maximum one process name with *
					if (entry.Rules.Where(a => a.ProcessName.Trim() == "*").Count() > 1)
						throw new InvalidConfigurationException("Every configuration entry must only have one rule where with Process Name = '*'!");

					// enusre, that every custom affinity mask rule has at least one processor set
					foreach(var rule in entry.Rules.Where(a=>a.AffinityType == Common.ProcessorAffinityType.Custom))
					{
						if (rule.CustomAffinityMask == 0)
							throw new InvalidConfigurationException("Every rule with a custom affinity mask must have at least one processor core assigned!");				
					
					}

					// ensure, that every rule as a process name
					if (entry.Rules.Where(a =>a.ProcessName.Trim() == string.Empty).Count() > 0)
						throw new InvalidConfigurationException("Every rule must have a process name!");
				}

				this.Model.StatusMessage = Messages.ConfigSaved;
			}

			private void SaveConfiguration()
			{
				var cfg = this.Model.configurationManager.Configuration;

				cfg.Interval = this.Model.Interval;
				cfg.ConfigurationEntries.Clear();

				foreach (var vmEntry in this.Model.ConfigurationEntries)
				{
					var entry = new Config.ConfigurationEntry();
					entry.IsActive = vmEntry.IsActive;
					entry.Name = vmEntry.Name;
					cfg.ConfigurationEntries.Add(entry);

					foreach(var vmRule in vmEntry.Rules)
					{
						var rule = new Config.ConfigurationRule();
						rule.AffinityMask = vmRule.CustomAffinityMask;
						rule.ProcessName = vmRule.ProcessName;
						rule.ProcessorAffinityType = vmRule.AffinityType;
						entry.Rules.Add(rule);
					}
				}

				this.Model.configurationManager.Save(cfg);
			}

			public override void Execute(object parameter)
			{
				try
				{
					this.ValidateConfiguration();
					this.SaveConfiguration();
				}
				catch (Exception ex)
				{
					MessageBox.Show("Error: \n" + ex.Message);
				}
			}
		}

		private class MyIncreaseIntervalCommand : MyCommandBase<ConfigurationViewModel>
		{
			public MyIncreaseIntervalCommand(ConfigurationViewModel model) : base(model)
			{}

			public override bool CanExecute(object parameter)
			{
				return true;
			}

			public override void Execute(object parameter)
			{
				this.Model.Interval++;
				this.Model.StatusMessage = Messages.ConfigNotSaved;
			}
		}

		private class MyDecreaseIntervalCommand : MyCommandBase<ConfigurationViewModel>
		{
			public MyDecreaseIntervalCommand(ConfigurationViewModel model) : base(model)
			{}

			public override bool CanExecute(object parameter)
			{
				return true;
			}

			public override void Execute(object parameter)
			{
				if (this.Model.Interval > 10)
				{
					this.Model.Interval--;
					this.Model.StatusMessage = Messages.ConfigNotSaved;
				}
			}
		}
		#endregion

		/// <summary>
		/// Gets the command which refreshes the process list
		/// </summary>
		/// <value>The refresh process list command.</value>
		public ICommand RefreshProcessListCommand { get; private set; }

		/// <summary>
		/// Gets the command which adds a new default entry to the parent
		/// </summary>
		/// <value>The add new entry command.</value>
		public ICommand AddNewEntryCommand { get; private set; }

		/// <summary>
		/// Gets the command which saves the whole configuration
		/// </summary>
		/// <value>The save command.</value>
		public ICommand SaveCommand { get; private set; }

		/// <summary>
		/// Gets command which increases the scan interval value by 1.
		/// </summary>
		/// <value>The increase interval command.</value>
		public ICommand IncreaseIntervalCommand { get; private set; }

		/// <summary>
		/// Gets command which decreses the scan interval value by 1.
		/// </summary>
		/// <value>The decrease interval command.</value>
		public ICommand DecreaseIntervalCommand { get; private set; }
	}
}